Meanwhile biorthogonal wavelets got a very popular image processing tool, alternative multiresolution transforms have been proposed for solving some of their drawbacks, namely the poor selectivity in orientation and the lack of translation invariance due to the aliasing between subbands. These transforms are generally overcomplete and consequently offer huge degrees of freedom in their design. At the same time their optimization get a challenging task. We proposed here a log-Gabor wavelet transform gathering the excellent mathematical properties of the Gabor functions with a carefully construction to maintain the properties of the filters and to permit exact reconstruction. Two major improvements are proposed: first the highest frequency bands are covered by narrowly localized oriented filters. And second, all the frequency bands including the highest and lowest frequencies are uniformly covered so as exact reconstruction is achieved using the same filters in both the direct and the inverse transforms (which means that the transform is self-invertible). The transform is optimized not only mathematically but it also follows as much as possible the knowledge on the receptive field of the simple cells of the Primary Visual Cortex (V1) of primates and on the statistics of natural images. Compared to the state of the art, the log-Gabor wavelets show excellent behavior in their ability to segregate the image information (e.g. the contrast edges) from incoherent Gaussian noise by hard thresholding and to code the image features through a reduced set of coefficients with large magnitude. Such characteristics make the transform a promising tool for general image processing tasks.
Reference (BibTex Format):
@article{Fischer07cv,
Author = {Fischer, Sylvain and Sroubek, Filip and Perrinet, Laurent U. and Redondo, Rafael and Crist{\'o}bal, Gabriel},
Journal = {Int. Journal of Computional Vision},
Keywords = {wavelet transforms, log-Gabor filters, oriented high-pass filters, image denoising, visual system},
Title = {Self-invertible 2{D} log-{G}abor wavelets},
Url = {http://invibe.net/LaurentPerrinet/Publications/Fischer07cv},
Year = {2007}}
Requirements :
To install them, use
pip install -r requirements.txt
Install using pip:
pip install LogGabor
%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
np.set_printoptions(precision=2, suppress=True)
from NeuroTools.parameters import ParameterSet
pe = ParameterSet('default_param.py')
opts= {'cmap': plt.cm.gray, 'vmin':-1., 'vmax':1., 'interpolation':'nearest', 'origin':'upper'}#, 'figsize':(16,4)}
from LogGabor import LogGabor as LG
#! this test report is about the LogGabor class:
print LG.__doc__
#! let's define the image processing framework
pe.N_X = 32
im = LG.Image(pe)
plt.subplot(111)
#! im.f gives the radial coordinates - note the overlay of coordinates in the matrix
plt.imshow(im.normalize(im.f), **opts)
plt.show()
print im.f[(im.n_x-1)//2 + 1, (im.n_y-1)//2 + 1], im.f[(im.n_x-1)//2 , (im.n_y-1)//2 ]
im = LG.Image(pe)
plt.subplot(111)
plt.imshow(im.normalize(im.f), **opts)
plt.show()
print im.f[(im.n_x-1)//2 + 1, (im.n_y-1)//2 + 1], im.f[(im.n_x-1)//2 , (im.n_y-1)//2 ]
#! defining input image as Lena
#!-----------------------------
image = plt.imread('database/lena512.png')[:,:,0]
print image.mean(), image.std()
pe.N_X = image.shape[0]
im = LG.Image(pe)
image = im.normalize(image, center=True)
print image.mean(), image.std()
lg = LG.LogGabor(im)
n = image.shape[0]
#! generating some LogGabor filters
#!---------------------------------
#! viewing is done by a dedicated function, show_loggabor :
print LG.LogGabor.show_loggabor.__doc__
#! defining a reference log-gabor
# (look in the corners!)
sf_0 = image.shape[0]/25. # TODO .1 cycle / pixel (Geisler)
params= {'sf_0':sf_0, 'B_sf': pe.B_sf, 'theta':0., 'B_theta': pe.B_theta}
fig = lg.show_loggabor(0, 0, **params)
#! translating in the middle
N_X, N_Y = image.shape
fig = lg.show_loggabor(n/2, n/2, **params)
#! same params, larger frequency with sf_0
params2 = {'sf_0':sf_0*4, 'B_sf':pe.B_sf, 'theta':0., 'B_theta':pe.B_theta}
fig = lg.show_loggabor(n/4, n/2, **params2)
#! narrower with B_theta
params3 = {'sf_0':sf_0, 'B_sf':pe.B_sf, 'theta':0., 'B_theta':pe.B_theta/4}
fig = lg.show_loggabor(n/4, 3*n/4, **params3)
#! broader spectrum with B_sf
params4 = {'sf_0':sf_0, 'B_sf':pe.B_sf*2., 'theta':0., 'B_theta':pe.B_theta}
fig = lg.show_loggabor(3*n/4, n/4, **params4)
#! a function to explore these parameters:
def lg_explore(param_name, param_range, angle=False):
for param_ in param_range:
if angle:
title = np.str(param_*180/np.pi) + '°'
else:
title = np.str(param_)
print param_name, title
params_=params.copy()
params_.update({param_name:param_})
fig, a1, a2 = lg.show_loggabor(n/2, n/2, **params_)
#_ = a1.set_xlabel(param_name)
#_ = a2.set_xlabel(title)
#! explore these parameters individually:
lg_explore(param_name='phase', param_range=np.linspace(0, np.pi, 6, endpoint=False), angle=True)
lg_explore(param_name='theta', param_range=np.linspace(0, np.pi, 6, endpoint=False), angle=True)
lg_explore(param_name='B_theta', param_range=np.pi/np.linspace(4, 14, 6), angle=True)
#! sf_0 pyramid that we use later in Matching Pursuit
base = 2 #1.618
n_levels = int(np.log(np.max((N_X, N_Y)))/np.log(base))#+1
v_sf_0 = N_X / np.logspace(1, n_levels, n_levels, base=base)
#v_sf_0 = 2. ** np.arange(n_levels)
print v_sf_0, lg.n_x / v_sf_0, len(v_sf_0)==n_levels
lg_explore(param_name='sf_0', param_range=v_sf_0)
relative distances :
scaling = np.sqrt(v_sf_0[:, np.newaxis]*v_sf_0[np.newaxis, :])/np.sqrt(N_X)
print v_sf_0
print scaling, scaling.mean()
lg_explore(param_name='B_sf', param_range=np.logspace(-1, 0, 5)*pe.B_sf)
plt.subplot(111)
plt.imshow(im.normalize(image), **opts)
v = plt.axis([0, n, n, 0])
#! whitening to balance the energy of evey frequency band
white = im.whitening(image)
plt.imshow(im.normalize(white), **opts)
v = plt.axis([0, n, n, 0])
#! the filtering operation preserves infomation (none is lost...)
plt.imshow(white - im.FTfilter(white, 1.), **opts)
#! a function to explore these parameters:
def filter_explore(param_name, param_range):
for param_ in param_range:
print param_name, param_
params_=params.copy()
params_.update({param_name:param_})
FT_lg = lg.loggabor(0, 0, **params_)
im_ = im.FTfilter(image, FT_lg, full=True)
plt.figure(figsize=(18, 6))
plt.subplot(122)
im_phi = np.angle(im_)
plt.imshow(im_phi, cmap=plt.cm.hsv)
v = plt.axis([0, n, n, 0])
plt.subplot(121)
im_a = np.absolute(im_)
im_a = 2*im.normalize(im_a)-1 # back in the range -1, 1
plt.imshow(im_a, **opts)
v = plt.axis([0, n, n, 0])
plt.show()
#! explore parameters of LogGabors on the filtering:
filter_explore(param_name='theta', param_range=np.linspace(0, np.pi, 6, endpoint=False))
filter_explore(param_name='B_theta', param_range=np.pi/np.linspace(4, 14, 6))
print lg.n_x / v_sf_0
filter_explore(param_name='sf_0', param_range=v_sf_0)
filter_explore(param_name='B_sf', param_range=np.logspace(-1, 1, 5)*pe.B_sf)
#! TODO: show a tiling of Fourier space with one particular choice of log gabors
#n_levels = 6
##! zooming on some intersing part of the scale space
#for sf_0_ in lg.n_x / 2. / np.logspace(n_levels-3, n_levels-1, n_levels, base=2):
# print 'sf_0 ' , sf_0_
# FT_lg = lg.loggabor(0, 0, sf_0=sf_0_, B_sf=pe.B_sf, theta=0., B_theta=B_theta)
# im_ = im.FTfilter(white, FT_lg)
# im_ = im.normalize(im_)
# pylab.imshow(im_, **opts)
# pylab.show()
%install_ext https://raw.githubusercontent.com/rasbt/python_reference/master/ipython_magic/watermark.py
%load_ext watermark
%watermark
# CSS styling within IPython notebook
from IPython.core.display import HTML
def css_styling():
styles = open("custom.css", "r").read()
return HTML(styles)
css_styling()